﻿/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A
 * copy of the license can be found at http://go.microsoft.com/fwlink/?LinkID=131993. If
 * you cannot locate the Microsoft Public License, please send an email to
 * mixon@microsoft.com. By using this source code in any fashion, you are agreeing to
 * be bound by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace MIXOnline.Descry
{

    /// <summary>
    /// This control houses the entire Timeline which contains the ScalePanels and the VirtualizedItemsControl,
    /// which in turn houses the SurfacePanels. The SurfacePanels contain the VirtualizedTimelineStackPanels
    /// </summary>
    public partial class Timeline : UserControl
    {
        public Timeline()
        {
            InitializeComponent();

            this.virtualizedTimelineItemsControl.Loaded += new RoutedEventHandler(virtualizedTimelineItemsControl_Loaded);
            this.virtualizedTimelineItemsControl.MouseMove += new MouseEventHandler(virtualizedTimelineItemsControl_MouseMove);

            // this is employed to allow for smooth animation
            CompositionTarget.Rendering += delegate(object sender, EventArgs e)
            {
                CompositionTarget_Rendering(sender, e);
            };
        }

        #region Properties

        public VirtualizedTimelineItemsControl Vic
        {
            get { return this.virtualizedTimelineItemsControl; }
        }

        public View View
        {
            get { return _view; }
            set
            {
                _view = value;

                Storage.SetAppSettingItem("View", _view.Name); // persist user selected view name

                virtualizedTimelineItemsControl.View = _view;

                DateTime StartingTime = Views.GetStartingTimeForView(_view); // SurfacePanels and ScalePanels Start times change depending on view
                ScalePanel1.StartingDateTime = StartingTime;
                ScalePanel0.StartingDateTime = StartingTime - _view.TimeSpan;
                ScalePanel2.StartingDateTime = StartingTime + _view.TimeSpan;

                // let the ScalePanels know that the view has changed.
                ScalePanel1.View = ScalePanel0.View = ScalePanel2.View = _view;

                // re-initialize ScalePanel Rotation positions
                LeftScalePanel = ScalePanel0;
                MiddleScalePanel = ScalePanel1;
                RightScalePanel = ScalePanel2;

                InitializeScalePanelPositions(); // reposition grids to position 0

                this.OnViewChanged(new TimelineEventArgs(null));
            }
        }

        public Views Views
        {
            get { return _views; }
            set { _views = value; }
        }

        #endregion Properties

        #region ScalePanel Movement

        /// <summary>
        /// Takes care of animating physics movement smoothly
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            // if the Timeline has moved update the ScalePanel Position
            if (virtualizedTimelineItemsControl.SurfacePanel0Position != _lastTimelinePosition)
            {
                HelperMethods.setX(ScalePanel0, virtualizedTimelineItemsControl.SurfacePanel0Position);
                HelperMethods.setX(ScalePanel1, virtualizedTimelineItemsControl.SurfacePanel1Position);
                HelperMethods.setX(ScalePanel2, virtualizedTimelineItemsControl.SurfacePanel2Position);

                if (Math.Abs(virtualizedTimelineItemsControl.SurfacePanel0Position - _lastTimelinePosition) > 1 ) // if it's moved more than a pixel in either direction
                    entryItemPopup.Visibility = Visibility.Collapsed; // if the ScalePanel is moving, hide the EntryItemPopup

                SwapCheck();

                // for debug
                //UpdateHorizontalSlidePosition();

                _lastTimelinePosition = virtualizedTimelineItemsControl.SurfacePanel0Position;
            }
        }

        /// <summary>
        /// Do the ScalePanels need to be swapped?
        /// </summary>
        private void SwapCheck()
        {
            double middleGridPosition = HelperMethods.getX(MiddleScalePanel);
            double timeGridContainerWidth = SurfaceContainer.ActualWidth;
            if (middleGridPosition < -timeGridContainerWidth / 2 || middleGridPosition > timeGridContainerWidth / 2)
                SwapPanels(middleGridPosition, timeGridContainerWidth);
        }

        /// <summary>
        /// Takes care of swapping (or rotating) the ScalePanels to keep up with the timeline
        /// </summary>
        /// <param name="middleGridPosition">The left coordinate of the MiddleGrid</param>
        /// <param name="containerWidth">Current width of Timeline</param>
        private void SwapPanels(double middleGridPosition, double containerWidth)
        {
            ScalePanel OriginalLeftScalePanel = LeftScalePanel;
            ScalePanel OriginalMiddleScalePanel = MiddleScalePanel;
            ScalePanel OriginalRightScalePanel = RightScalePanel;

            // when moving to the left (sliding into future)
            // rotate grid to right and update with new content
            if (middleGridPosition < -containerWidth / 2)
            {
                // reposition LeftGrid one unit to the Right of RightGrid
                HelperMethods.setX(LeftScalePanel, HelperMethods.getX(RightScalePanel) + containerWidth);

                //swap panels
                LeftScalePanel = OriginalMiddleScalePanel;
                MiddleScalePanel = OriginalRightScalePanel;
                RightScalePanel = OriginalLeftScalePanel;

                // set TimeStamp on rotated panel
                RightScalePanel.StartingDateTime = OriginalRightScalePanel.StartingDateTime + _view.TimeSpan;
            }

            // when moving to right (sliding into past) 
            // rotate grid to left and update with new content.
            if (middleGridPosition > containerWidth / 2)
            {
                // reposition RightGrid one unit to the Left of LeftGrid
                HelperMethods.setX(RightScalePanel, HelperMethods.getX(LeftScalePanel) - containerWidth);

                // swap panels
                LeftScalePanel = OriginalRightScalePanel;
                MiddleScalePanel = OriginalLeftScalePanel;
                RightScalePanel = OriginalMiddleScalePanel;

                // set TimeStamp on rotated panel
                LeftScalePanel.StartingDateTime = OriginalLeftScalePanel.StartingDateTime - _view.TimeSpan;
            }
        }

        #endregion ScalePanel Movement

        #region Events

        #region Event Handlers

        public event SurfaceEventHandler ViewChanged;

        protected virtual void OnViewChanged(TimelineEventArgs e)
        {
            SurfaceEventHandler handler = this.ViewChanged;
            if (handler != null)
                handler(this, e);
        }

        #endregion Event Handlers

        /// <summary>
        /// // set Timeline intiial view and subscribe to events
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void virtualizedTimelineItemsControl_Loaded(object sender, RoutedEventArgs e)
        {
            InitControl();
        }

        /// <summary>
        /// Reposition EntryItems in resized panel
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Surface_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            // init positions
            double middlePosition = HelperMethods.getX(MiddleScalePanel);
            double width = SurfaceContainer.ActualWidth;
            HelperMethods.setX(LeftScalePanel, middlePosition - width);
            HelperMethods.setX(RightScalePanel, middlePosition + width);

            // for debug
            //UpdateHorizontalSlidePosition();
        }

        /// <summary>
        /// Hides EntryItemPopup
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void virtualizedTimelineItemsControl_MouseMove(object sender, MouseEventArgs e)
        {
            string name = (e.OriginalSource as FrameworkElement).Name;
            if (name != "image" && name != "avatar" && name != "EntryItem" && name != "rect")
                entryItemPopup.Visibility = Visibility.Collapsed;
        }

        /// <summary>
        /// Positions and Shows EntryItemPopup
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void entryItem_MouseEnter(object sender, MouseEventArgs e)
        {
            StackPanel entryItem = sender as StackPanel;
            entryItemPopup.DataContext = entryItem.DataContext;

            // Get abs position of the EntryItem
            Point offset = HelperMethods.GetCoords(this, entryItem);

            // position EntryItemPopup
            Canvas.SetLeft(entryItemPopup, offset.X + 32);
            Canvas.SetTop(entryItemPopup, offset.Y + 3);

            entryItemPopup.Visibility = Visibility.Visible;
        }

        #endregion Events

        #region Methods

        /// <summary>
        /// for debug purposes. Keeps timestamp current in UI
        /// </summary>
        private void UpdateHorizontalSlidePosition()
        {
            if (SurfaceContainer.Visibility != Visibility.Collapsed)
                this.textBoxCurrentTimeStamp.Text = virtualizedTimelineItemsControl.GetTimelineCenterDateTime().ToString();
        }

        /// <summary>
        /// Put ScalePanels in initial position
        /// </summary>
        private void InitializeScalePanelPositions()
        {
            double width = SurfaceContainer.ActualWidth;
            HelperMethods.setX(MiddleScalePanel, 0);
            HelperMethods.setX(LeftScalePanel, -width);
            HelperMethods.setX(RightScalePanel, width);
        }

        /// <summary>
        /// Called only on Surface instantiation
        /// </summary>
        private void InitControl()
        {
            // Set view to users last View choice or default to "Hour"
            string view = Storage.GetAppSettingItem("View") as string;
            if (string.IsNullOrEmpty(view))
                View = _views.GetView("Hour");
            else
                View = _views.GetView(view);

            // subscribe to events
            this.SizeChanged += new SizeChangedEventHandler(Surface_SizeChanged);
        }

        #endregion Methods

        #region Privates

        private double _lastTimelinePosition = 0;
        private const int DX = 30;
        private ScalePanel LeftScalePanel, MiddleScalePanel, RightScalePanel;
        private List<SurfacePanel> _surfacePanels = new List<SurfacePanel>();
        private Views _views = new Views();
        private View _view;
        //Point _lastEntryItemMousePos;

        #endregion Privates

    }

}
